import Module from 'module'
import fs from 'fs';
import path from 'path';


const grammar = ["static","if","return","while","await","let","var","for"];
// 匹配module的正则
export const MODULE = `(?<=^[\\s]*)module.exports`;
// 匹配exports的正则
export const EXPORTS = `(?<=^\\s*exports\\.).*$`;
// 匹配class的正则
export const CLASS = `(?<=^\\s*)[_\\w\\d]+(?=\\s+\\()|(?<=^\\s*static\\s+async\\s+).*(?=\\s*\\()|(?<=static\\s+)(?!async)[_\\w\\d]*`;
// 匹配object的正则
export const OBJECT = `(?<=^\\s*(async)*\\s*(?!async|\\b${grammar.join("\\b|")}))\\b[\\$_\\w\\d]+\\b(?=[\\(: ]+.*?\\)[\\s=>]*{)`
// 匹配注解的正则
export const ANNOTATION = `(?<=(^[\\s"'\`/*]*))@[_\\w\\d]*\\s*(\\(.*?\\))?(?=\\s*[\\s*"'\`\\n]*)`;

// 注解
const Annotation = {};

if(module) module.exports = {injectRequire,runAnnotation,getAnnotation,addAnnotation}
export default {injectRequire,runAnnotation,getAnnotation,addAnnotation};

// 这里主要是对require进行插桩,注入
function injectRequire(name:string|Array<any>,path?:string){
    name && addAnnotation(name,path);
    let js = require.extensions[".js"];
    if(!require){ global.require = Module.createRequire(__filename);}
    // 生成清除注解
    require.extensions[".js"] = function(module:any,filename){
        let modules:any = new Module(filename)
        modules._compile=(content:string,filename:string)=>{
            try{
                let data = purifyContent(content);
                module._compile(data,filename);
            }catch(err){
                console.log(err.message)
                console.log(err)
            }
        }
        js(modules,filename);
    }

    // 净化上下文
    function purifyContent(content:string){
        let deleteAnnotation = [];
        let annotation = content.match(/(?<=^\s*@)[_\w\d]*\s*(\(.*?\))?(?=\s*[\s*"'\`\n]*)/gm);
        // 遍历 annotation 获取需要净化的
        annotation?.forEach(e=>{
            e = e.replace(/[(\r\n\s].*$/,"").trim();
            if(new RegExp(`(let |var import ).*?\\b${e}\\b`).test(content)){
                deleteAnnotation.push("@"+e)
            }else if(Annotation[e]){
                deleteAnnotation.push("@"+e)
            }
        })

        if(!deleteAnnotation.length) return content;
        return content.replace(new RegExp(`^\\s*(${deleteAnnotation.join("|")}).*`,"gm"),"");
    }
}

// 运行注解
async function runAnnotation(filepath:string,deleteCache:boolean = false){
    filepath = (Module as any)._resolveFilename(filepath,null,false);
    // 读取文件
    let content = fs.readFileSync(filepath,'utf8')
    // 解析出文件中的注解信息
    let annota:Array<annotationData> = matchAnnotation(content,filepath);
    if(annota.length === 0) return
    annota[annota.length-1].isEnd = true;

    for(let index in annota){
        let e = annota[index];
        // 开始执行
        let annotation = require(e.path)?.default || require(e.path);
        annotation[e.name] && (annotation = annotation[e.name]);
        // 如果缓存存在就删除缓存重新导
        if(require.cache[filepath] && deleteCache) delete require.cache[filepath];
        require(filepath)

        let module = require.cache[filepath];
        let method = module.exports.default || module.exports;
        await annotation({
            methodName:e.methodName,
            isEnd:e.isEnd,
            method:e.name==='this' ? method : (method[e.methodName] || method),
            module:module
        },...(e.parameter?.split(/(?<=["'`\s\d]),(?=["'`\s\d])/g).map(ee=>{
            ee = ee.replace(/^['`]|['`]$/g,"\"")
            try{
                return JSON.parse(ee);
            }catch(err){
                console.log(`
    file      : ${filepath}
    annotation: ${e.name}
    method    : ${e.methodName}
    parameter : ${e.parameter}
    Error     : 注解参数异常
                `)
                return ee;
            }
        }) || []))
    }
}


// 获取注解
function getAnnotation(){return Annotation};

// 添加注解的方法
function addAnnotation(name:string|Array<any>,path:string){
    if(Array.isArray(name)){
        name.forEach(e=>{
            Annotation[e.name] = (Module as any)._resolveFilename(e.path,null,false);
        })
    }else{
        Annotation[name] = (Module as any)._resolveFilename(path,null,false)
    }
};

//匹配所有的注解
function matchAnnotation(data:string,filepath:string):Array<annotationData>{
    if(!new RegExp(ANNOTATION,"m").test(data)) return [];
    let matchingModule = new RegExp(MODULE);
    // 全匹配 allMatch
    let allMatch = new RegExp(`${ANNOTATION}|${CLASS}|${EXPORTS}|${MODULE}|${OBJECT}`,"gm");
    // 保存注解 
    let saveAnnotation:Array<annotationData> = [];
    // 保存完全解析好的数据
    let all:Array<annotationData> = [];

    // 开始净化
    data.match(allMatch).map(e=>{
        // 这可能是一个注解
        if(/(?=@).*\)*/.test(e)){
            // 净化
            e = e.replace(/.*?@/g,"").trim();
            // 去除多余的字符 解析这个注解，包括注解的地址
            let annotation:annotationData = pars_annotation_parameter(data,e.replace(/(?<=\)).*|[ \n]*$/g,""),filepath);
            if(annotation){saveAnnotation.push(annotation);}

        }else if(matchingModule.test(e)){
            // 是module
            saveAnnotation.forEach(ee=>{
                // 方法名称
                ee.methodName = "this";
                all.push(ee);
            })
            saveAnnotation = [];
        }else{
            e = e.replace(/[\s:=].*/g,"").trim();
            saveAnnotation.forEach(ee=>{
                // 方法名称
                ee.methodName = e;
                all.push(ee);
            })
            saveAnnotation = [];
        }
    })
    return all;
}


function pars_annotation_parameter(data:string,str:string,filepath:string):annotationData{
    let obj = {
        path:"",
        // 注解名称
        name:str.replace(/[\( ].*$/,""),
        // 注解参数
        parameter:undefined,
        isEnd:false
    };
    // 判断这个注解有没有被导入
    let a = data.match(new RegExp(`(?<=(let |var import )\\s*({.*?\\b${obj.name}\\b.*?}|\\b${obj.name}\\b).*?(require|from)).*`,"g"))?.[0];
    if(a){
        // 把引用的地址匹配出来
        obj.path = a.match(/(?<="|`|').*(?="|`|')/)?.[0];

        // filepath 匹配到的可能是相对路径，开始解析
        if(obj.path.startsWith(".")){
            // 相对路径
            obj.path = path.join(path.dirname(filepath),obj.path)
        }
    }else{
        // 去存储中查找
        if(!Annotation[obj.name]){ return undefined }
        obj.path = Annotation[obj.name];
    }
    // 把括号中的参数解析出来
    let par = str.match(/(?<=\().*(?=\))/)?.[0];
    // 以逗号分割参数
    par && (obj.parameter = par);
    return obj;
}

// (?!\/|\/\*)\s*(?<=(module.exports|export default))

// 匹配标签
// (?<=<(.*?>)).*(?=</\1)
// https://www.zhihu.com/question/25002833